/*
 * Object   : CClient
 * Type     : Backend
 *
 * Narrator : Kamran H. Khan 
 * Email    : KAMRANHK@gmail.com
 * 
 * Copyleft : Attribution - CreativeCommons http://creativecommons.org/licenses/by-nc/3.0/
 *            This software is provided 'as-is', without any express or implied warranty.
 *            In no event will the author(s) be held liable for any damages arising from
 *            the use of this software.
 * 
 *            Permission is granted to anyone to use this software for any purpose,
 *            including commercial applications, and to alter it and redistribute it
 *            freely, subject to the following restrictions:
 * 
 *            1. The origin of this software must not be misrepresented; you must not
 *            claim that you wrote the original software. If you use this software
 *            in a product, an acknowledgment in the product documentation would be
 *            appreciated but is not required.
 * 
 *            2. Altered source versions must be plainly marked as such, and must not
 *            be misrepresented as being the original software.
 * 
 *            3. This notice may not be removed or altered from any source distribution.
 * 
 * Description: 
 * Provides the TCPIP client interface
 * 
 * Revision History:
 * 
 * Date(MMM/dd/yy)      Owner           Description
 * ----------------------------------------------------------------------------------------------------------------------------------------------
 * Oct/24/08            Kamran H. Khan  Added skeleton code
 * Oct/25/08            Kamran H. Khan  Added more methods
 *  
 * 
 * 
 * 
*/


using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Threading;
using Maxima.Help;

namespace Maxima.Networker
{
    [ToolboxBitmap(typeof(Bitmap), "MaximaLabs.Networker.Properties.Resources.mstsc")]
    public class CClient : CNetMgr, IDisposable
    {
        private TcpClient m_theClient;
        private NetworkStream m_nwkStream;
        private ASCIIEncoding theEncoder = new ASCIIEncoding();
        private string m_strIP;
        private Thread m_thdConnector;
        private Thread m_thdClientHandler;
        private string m_strStatus = string.Empty;

        private const int READ_BUFFER_MAX_SIZE = 4096;
        private const int WAIT_FOR_SOME_SECONDS = 3;

        public delegate void StatusChangeEventHandler(string strData);
        public event StatusChangeEventHandler OnStatusChange;

        private bool m_bIsConnected = false;
        public bool IsConnected
        {
            get { return m_bIsConnected; }
        }

        [Category("Settings")]
        [Description("IP Address")]
        [BrowsableAttribute(true)]
        public string IP
        {
            get { return m_strIP; }
            set { m_strIP = value; }
        }
        private int m_nPort;
        [Category("Settings")]
        [Description("Port")]
        [BrowsableAttribute(true)]
        public int Port
        {
            get { return m_nPort; }
            set { m_nPort = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CClient"/> class.
        /// </summary>
        /// <param name="strIP">The STR IP.</param>
        /// <param name="nPort">The n port.</param>
        public CClient(string strIP, int nPort)
            : base(strIP, nPort, false)
        {
            m_nPort = nPort;
            m_strIP = strIP;
        }

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            // use threadings.
            m_thdConnector = new System.Threading.Thread(delegate() { Connector(); });
            m_thdConnector.IsBackground = true;
            m_thdConnector.Start();

        }



        /// <summary>
        /// Connectors this instance.
        /// </summary>
        private void Connector()
        {
            m_theClient = new TcpClient();

            IPEndPoint epServerID = new IPEndPoint(IPAddress.Parse(m_strIP), Convert.ToInt32(m_nPort));

            //Continue, unless it gets connected.
            while (!m_theClient.Connected)
            {
                try
                {
                    Log("Trying to connect... to: " + m_strIP + ":" + m_nPort.ToString());
                    m_theClient.Connect(epServerID);
                    m_bIsConnected = true;
                    break;//here, this means it got the connection successfully; lets do the rest.
                }
                catch (Exception exc)
                {
                    Log("Couldnt find server.");
                    CHelper.Log(exc.Message);
                }

                Wait(); //

            }

            Log("Connected. [" + m_strIP + ":" + m_nPort.ToString() + "]");
            m_nwkStream = m_theClient.GetStream();

            m_thdClientHandler = new System.Threading.Thread(delegate() { HandleClient(); });
            m_thdClientHandler.IsBackground = true;

            m_thdClientHandler.Start();

        }

        /// <summary>
        /// Waits this instance.
        /// </summary>
        private void Wait()
        {
            Log("Will try in secs: " + WAIT_FOR_SOME_SECONDS.ToString());
            System.Threading.Thread.Sleep(WAIT_FOR_SOME_SECONDS * 1000);
        }

        /// <summary>
        /// Handles the client.
        /// </summary>
        private void HandleClient()
        {
            ASCIIEncoding theEncoding = new ASCIIEncoding();
            byte[] bytMessage = new byte[READ_BUFFER_MAX_SIZE];

            try
            {
                while (true)
                {
                    int nBytesRead = 0;

                    if (m_theClient == null || m_nwkStream == null)
                        break;

                    if (!m_theClient.Connected)
                        break;

                    if (m_nwkStream.CanRead && m_nwkStream != null)
                        nBytesRead = m_nwkStream.Read(bytMessage, 0, bytMessage.Length);

                    if (nBytesRead == 0)
                        continue;

                    string strMsg = theEncoding.GetString(bytMessage);
                 

                    Log("\r\nRX: MSG[" + strMsg);
                    Log("MSG[" + strMsg);

                    //Log("\r\nRX: MSG[" + m_strIP + ":" + m_nPort + "]" + strMsg);
                    //Log("MSG[" + m_strIP + ":" + m_nPort + "]" + strMsg);


                    if (strMsg != null && strMsg == "logout")
                        break;
                }
            }
            catch (Exception exc)
            {
                Error(exc.Message);
            }
        }

        /// <summary>
        /// Sends the specified STR data.
        /// </summary>
        /// <param name="strData">The STR data.</param>
        /// <returns></returns>
        public int Send(string strData)
        {
            int nSentBytes = 0;
            if (m_nwkStream == null)
            {
                Log("Nowhere to send.");
                return 0;//means, this client was not connected to any server, lets not send anything.
            }
            else
            {
                byte[] bytBuffer = theEncoder.GetBytes(strData);

                m_nwkStream.Write(bytBuffer, 0, bytBuffer.Length);
                m_nwkStream.Flush();
                bytBuffer = null;
                nSentBytes = strData.Length;
                Log("\r\nTX: " + strData);//Msg sent, bytes[" + nSentBytes.ToString() + "]");
            }
            return nSentBytes;
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {

            if (m_thdConnector != null)// m_thdClientHandler != null)
            {
                m_bIsConnected = false;
                m_thdConnector.Abort();// m_thdClientHandler.Abort();
                Log("Stopped.");

            }
            else
            {
                Log("Couldnt stop, no thread was found.");
            }

        }

        /// <summary>
        /// Errors the specified STR MSG.
        /// </summary>
        /// <param name="strMsg">The STR MSG.</param>
        private void Error(string strMsg)
        {
            Log("ERROR: " + strMsg);
        }

        /// <summary>
        /// Logs the specified STR MSG.
        /// </summary>
        /// <param name="strMsg">The STR MSG.</param>
        private void Log(string strMsg)
        {
            m_strStatus = strMsg;//"\r\nCLI:" +

            if (OnStatusChange != null)
                OnStatusChange(m_strStatus);
        }

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        public void Dispose()
        {
            m_nwkStream.Dispose();

        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            Dispose();
        }

        #endregion

        /// <summary>
        /// Initializes the component.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.ResumeLayout(false);

        }
    }
}
